package calculatorBase;

import java.util.ArrayList;
import java.util.Random;

import formuParse.*;

public class CalculatorBase
{
	public ArrayList<String> variable_rep = new ArrayList<String>();
	public ArrayList<Double> variable_value = new ArrayList<Double>();
	public ArrayList<Formula> formula_system = new ArrayList<Formula>();
	public ArrayList<Formula> to_graph = new ArrayList<Formula>();
	public String builtInFunctions[]={"sum","product","clearAll","delete","isPrime","abs","derive","integrate","seed","rnd","rndn","rndi","rndb","ln","npr","ncr","log","round","ipart","fpart","int","floor","ciel","sign","cos","sin","tan","acos","asin","atan","csc" ,"sec","cot","acsc","asec","acot","cosh","sinh","tanh"};
	public Random rand_gen;
	public Formula eval_form = new Formula(this);
	public String st;
	public String rpn;
	public int parse_error = 0;
	public int eval_error = 0;
	public settings set;
	public long start;
	public long end;
	VariableFunctionChangeListener listen=null;
	
	public CalculatorBase(settings prefs)
	{
		set = prefs;
		set_variable("e", Math.E);
		set_variable("π", Math.PI);
		set_variable("pi", Math.PI);
		set_variable("BIG_NUMBER", Double.MAX_VALUE);
		set_variable("SMALL_DECIMAL", Double.MIN_VALUE);
		rand_gen = new Random();
	}

	public void set_variable(String var, double val)
	{
		int i;
		boolean temp = false;

		for (i = 0; i < variable_rep.size(); i++)
		{
			if (variable_rep.get(i).equals(var))
			{
				temp = true;
				variable_value.set(i, val);
			}
		}
		if (temp == false)
		{
			variable_rep.add(var);
			variable_value.add(val);
		}
		if(listen!=null)
		{
			listen.onVariablesChange(variable_rep, variable_value);
			listen.onChange(variable_rep, variable_value,formula_system);
		}
	}

	public double get_variable(String var)
	{
		int i;
		for (i = 0; i < variable_rep.size(); i++)
		{
			if (variable_rep.get(i).equals(var))
			{
				return variable_value.get(i);
			}
		}
		return 0;
	}

	public double eval_expression(String exp)
	{
		start = System.nanoTime();
		try
	    {
			parse_error = 0;
			if (exp.equals(""))
			{
				exp = "Ans";
			}
			if (!eval_form.parse(exp))
			{
				if (parse_error == 0)
				{
					parse_error = -1;
				}
			}
			else
			{
				st = eval_form.st();
				double ans = eval_form.eval(1);
				if (eval_error == 0)
				{
					set_variable("Ans", ans);
					end = System.nanoTime();
					return ans;
				}
			}
		}
		catch (Exception e)
		{
			parse_error = -2;
		}
		return 0;
	}

	public String get_error_string()
	{
		int in = 0;
		if (parse_error == 0 && eval_error == 0)
		{
			return "No error";
		}

		if (eval_error != 0)
		{
			in = eval_error;
		}
		if (parse_error != 0)
		{
			in = parse_error;
		}
		if (in == 1)
		{
			return "Syntax Error";
		}
		if (in == 2)
		{
			return "Type Mismatch";
		}
		if (in == 3)
		{
			return "Invalid Variable name";
		}
		if (in == 4)
		{
			return "Incorrect number of parameters for function";
		}
		if (in == 5)
		{
			return "Mismatched parentheses";
		}
		if (in == -1)
		{
			return "An unknown error occured";
		}
		if (in == -2)
		{
			return "An evil error occured, please report your input to support@iammecone.com";
		}
		return "An unknown error occured";
	}

	public boolean calc_error()
	{
		if (parse_error == 0 && eval_error == 0)
		{
			return false;
		}
		return true;
	}

	public boolean add_function(String name, String f, String of)
	{
		int i = 0;
		for (i = 0; i < formula_system.size(); i++)
		{
			if (formula_system.get(i).name == name)
			{
				formula_system.get(i).parse(f);
			}
		}
		Formula gen = new Formula(this);
		gen.parse(f);
		gen.basis = of;
		gen.name = name;
		formula_system.add(gen);
		if(listen!=null)
		{
			listen.onFunctionChange(formula_system);
			listen.onChange(variable_rep, variable_value,formula_system);
		}
		return calc_error();
	}

	public int get_eval_time()
	{
		if(!calc_error())
		{
		return (int) (end - start);
		}
		else
		{
			return -1;
		}
	}

	public int get_error()
	{
		int in = 0;

		if (eval_error != 0)
		{
			in = eval_error;
		}
		if (parse_error != 0)
		{
			in = parse_error;
		}
		return in;
	}
	public void setListener(VariableFunctionChangeListener listener)
	{
		listen=listener;
	}
	
	public void invalidate()
	{
		listen.onChange(variable_rep, variable_value,formula_system);
	}
	public boolean functionExists(String name)
	{
		for(int index=0;index<formula_system.size();index++)
		{
			if(formula_system.get(index).name.equals(name))
			{
				return true;
			}
		}
		return false;
	}
}

